Linked list components

Time: O(M+N); Space: O(M); medium

We are given head, the head node of a linked list containing unique integer values.

We are also given the list G, a subset of the values in the linked list.

Return the number of connected components in G, where two values are connected if they appear consecutively in the linked list.

Example 1:

Input: head = {ListNode} 0->1->2->3->None, G = [0, 1, 3]

Output: 2

Explanation:

  • 0 and 1 are connected, so [0, 1] and [3] are the two connected components.

Example 2:

Input: head = {ListNode} 0->1->2->3->4->None, G = [0, 3, 1, 4]

Output: 2

Explanation:

  • 0 and 1 are connected, 3 and 4 are connected, so [0, 1] and [3, 4] are the two connected components.

Constraints:

  • If N is the length of the linked list given by head, 1 <= N <= 10000.

  • The value of each node in the linked list will be in the range [0, N - 1].

  • 1 <= len(G) <= 10000.

  • G is a subset of all values in the linked list.

[5]:
class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None

1. Grouping

Intuition

Instead of thinking about connected components in G, think about them in the linked list. Connected components in G must occur consecutively in the linked list.

Algorithm

Scanning through the list, if node.val is in G and node.next.val isn’t (including if node.next is null), then this must be the end of a connected component.

For example, if the list is 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7, and G = [0, 2, 3, 5, 7], then when scanning through the list, we fulfill the above condition at 0, 3, 5, 7, for a total answer of 4.

[6]:
class Solution1(object):
    """
    Time: O(N+len(G)), where N is the length of the linked list with root node head.
    Space: O(len(G)), to store G set.
    """
    def numComponents(self, head, G):
        Gset = set(G)
        cur = head
        ans = 0
        while cur:
            if (cur.val in Gset and
                    getattr(cur.next, 'val', None) not in Gset):
                ans += 1
            cur = cur.next

        return ans
[7]:
s = Solution1()

head = ListNode(0)
head.next = ListNode(1)
head.next.next = ListNode(2)
head.next.next.next = ListNode(3)
G = [0, 1, 3]
assert s.numComponents(head, G) == 2

head = ListNode(0)
head.next = ListNode(1)
head.next.next = ListNode(2)
head.next.next.next = ListNode(3)
head.next.next.next.next = ListNode(4)
G = [0, 3, 1, 4]
assert s.numComponents(head, G) == 2
[8]:
class Solution1a(object):
    """
    Time: O(N+len(G)), where N is the length of the linked list with root node head.
    Space: O(len(G)), to store G set.
    """
    def numComponents(self, head, G):
        """
        :type head: ListNode
        :type G: List[int]
        :rtype: int
        """
        lookup = set(G)
        dummy = ListNode(-1)
        dummy.next = head
        curr = dummy
        result = 0

        while curr and curr.next:
            if curr.val not in lookup and curr.next.val in lookup:
                result += 1
            curr = curr.next

        return result
[9]:
s = Solution1a()

head = ListNode(0)
head.next = ListNode(1)
head.next.next = ListNode(2)
head.next.next.next = ListNode(3)
G = [0, 1, 3]
assert s.numComponents(head, G) == 2

head = ListNode(0)
head.next = ListNode(1)
head.next.next = ListNode(2)
head.next.next.next = ListNode(3)
head.next.next.next.next = ListNode(4)
G = [0, 3, 1, 4]
assert s.numComponents(head, G) == 2